using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;

using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.BZip2;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;

namespace Rocket.Core.Common
{
    /// <summary>
    /// Winzip related compression work can 
    /// be handle with the methods provided in this class
    /// </summary>
    public class ZipManager
    {
        /// <summary>
        /// Unzip a file that is compressed using winzip.
        /// The file will be unzip to the same 
        /// directory as the directory that the zip file is saved.
        /// </summary>
        /// <param name="fileName">Fully qualified name to the zip file</param>
        /// <returns>Operation Success or Failed</returns>
        public static List<string> UnzipFile(string fileName)
        {
            ZipInputStream s = new ZipInputStream(File.OpenRead(fileName));

            ZipEntry theEntry;
            List<string> fileList = new List<string>();
            while ((theEntry = s.GetNextEntry()) != null)
            {
                string directoryName = Path.GetDirectoryName(theEntry.Name);
                string f = Path.GetFileName(theEntry.Name);

                // create directory
                Directory.CreateDirectory(directoryName);
                
                if (f != String.Empty)
                {
                    FileStream streamWriter = File.Create(theEntry.Name);
                    fileList.Add(theEntry.Name);
                    int size = 2048;
                    byte[] data = new byte[2048];
                    while (true)
                    {
                        size = s.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }

                    streamWriter.Close();
                }
            }
            s.Close();
            return fileList;
        }

        /// <summary>
        /// Create a zip file (winzip) given a list of files.
        /// </summary>
        /// <param name="sourceFileList">The list of files that needed to be compressed</param>
        /// <param name="desFilePath">The file save path of the compressed zip file</param>
        /// <param name="dirName">The directory name that to be used when creating file entries. 
        /// This option can be used to avoid un wanted internal directories getting 
        /// created at the time of uncompressing.
        /// If you are using this option please make sure to give a directory 
        /// name that is common to all the file in the file list.</param>
        /// <returns>Operation a Success or Failure</returns>
        public static bool ZipFiles(ArrayList sourceFileList, string desFilePath, string dirName)
        {
            if ((sourceFileList != null) && (sourceFileList.Count > 0))
            {
                //string[] filenames = sourceFiles;

                Crc32 crc = new Crc32();

                ZipOutputStream s = new ZipOutputStream(File.Create(desFilePath));

                s.SetLevel(6); // 0 - store only to 9 - means best compression

                foreach (string file in sourceFileList)
                {
                    FileStream fs = File.OpenRead(file);

                    byte[] buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                    ZipEntry entry = new ZipEntry(file.Replace(dirName, ""));

                    entry.DateTime = DateTime.Now;

                    // set Size and the crc, because the information
                    // about the size and crc should be stored in the header
                    // if it is not set it is automatically written in the footer.
                    // (in this case size == crc == -1 in the header)
                    // Some ZIP programs have problems with zip files that don't store
                    // the size and crc in the header.
                    entry.Size = fs.Length;
                    fs.Close();

                    crc.Reset();
                    crc.Update(buffer);

                    entry.Crc = crc.Value;
                    s.PutNextEntry(entry);
                    s.Write(buffer, 0, buffer.Length);
                }

                s.Finish();
                s.Close();
                return true;
            }
            else
            {
                throw new Exception("Source File List is empty, the operation is aborted");
            }
        }

        /// <summary>
        /// Create a zip file (winzip) given a list of files.
        /// </summary>
        /// <param name="sourceFileList">The list of files that needed to be compressed</param>
        /// <param name="desFilePath">The file save path of the compressed zip file</param>
        /// <returns>Operation a Success or Failure</returns>
        public static bool ZipFiles(string[] sourceFileList, string desFilePath)
        {
            if ((sourceFileList != null) && (sourceFileList.Length > 0))
            {
                string[] filenames = sourceFileList;

                Crc32 crc = new Crc32();

                ZipOutputStream s = new ZipOutputStream(File.Create(desFilePath));

                s.SetLevel(6); // 0 - store only to 9 - means best compression

                foreach (string file in filenames)
                {
                    FileStream fs = File.OpenRead(file);

                    byte[] buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                    ZipEntry entry = new ZipEntry(file);

                    entry.DateTime = DateTime.Now;

                    // set Size and the crc, because the information
                    // about the size and crc should be stored in the header
                    // if it is not set it is automatically written in the footer.
                    // (in this case size == crc == -1 in the header)
                    // Some ZIP programs have problems with zip files that don't store
                    // the size and crc in the header.
                    entry.Size = fs.Length;
                    fs.Close();

                    crc.Reset();
                    crc.Update(buffer);

                    entry.Crc = crc.Value;
                    s.PutNextEntry(entry);
                    s.Write(buffer, 0, buffer.Length);
                }

                s.Finish();
                s.Close();
                return true;
            }
            else
            {
                throw new Exception("Source File List is empty, the operation is aborted");
            }
        }
    }
}
